
from common.Common import Point
from common.Constant import Constant
import numpy as np

# TODO
# 1、 考虑 user 和 service 区分。 server面向的只有service 不是user。 答：不需要区分，在这个实验中，一个用户一个 所以不需要区分
# 2、 request size 在哪用的   答：传输延迟中 请求数据的大小
# 3、 state是否可一只存储 user_id 和 service_id 以及server_id 因为这玩意不变。 待验证。 答 觉得user_id不行 因为药考虑动态变化的量
# 4、 state里有没有速度这一状态 对结果有影响吗？
# TODO 将state改为numpy不要用类  同事reward的计算使用state进行，不要牵扯timeslot和具体的类
# 考虑 迁移决策后的传输延迟 到底是跟哪个时隙计算的 （因该是当下服务器在哪，以及当下用户在哪。 迁移后的呢个时隙计算 也是如此）

'''
环境 三台mec服务器 间隔2000m m1,m2,m3
车辆在高速路上随机跑 高速路长5000米 宽18米

辆以[60,110]公里/小时范围内随机选择的固定速度移动
一旦车辆到达高速公路的边界，它就会再次出现在对面。  0,0  5000,18

一旦选择了车辆的随机速度，该车辆在整个模拟周期内都以该速度移动
'''


# 边缘服务器
class EdgeServer:
    def __init__(self, edge_id: int, location: Point, pn, bandwidth_between_user_server):
        self.edge_id = edge_id  # 边缘服务器id, 作为环境中边缘服务器列表下标
        self.location = location
        # 服务器到车辆的总带宽 上行 和 下行
        self.bandwidth_between_user_server_total = bandwidth_between_user_server
        # 服务器之间通信的总带宽 上行 和 下行
        # 服务器覆盖范围

        self.no = 1  # 信噪比
        self.user_list = []

        # W 带宽 10 MHz
        # F 边缘服务器总计算能力
        self.F = 100  # 边缘服务器总计算能力
        self.pn = pn  # Transmit power of each gNB
        # K 用户数量
        # Dn, Cn 任务量大小，所需cpu周期数, (300~500kb), (900, 1100)兆周期数 1Mhz = 1000khz = 1000*1000hz
        # f 用户本地计算能力 1GHz/s | [0.5, 1.5]GHz/s (1000*1000*1000)
        # dist, pi 上传功率，闲时功率 | mW (毫瓦) 用户距离
        # pn
        # state 系统状态


# 移动用户（包括车辆等）
class User:
    def __init__(self, user_id: int, location: Point, service):
        self.user_id = user_id
        self.location = location  # 用户坐标
        self.service = service


# MEC网络的总控制器
class SDNController:
    # 每个MEC服务器都扮演一个代理的角色，它独立地做出服务放置和迁移决策
    # 一旦每个MEC代理做出决策，它就会将其传达给SDN控制器，该控制器扮演中央代理的角色，以协调所有MEC服务器的决策。

    def check_action(self, action):
        '''
        检查action合法性
        :return:
        '''
        # user_num = len(action)
        # for i in range(user_num):
        #     tmp = sum([row[i] for row in action])
        #     if tmp != 1:
        #         return False
        # return True
        return True

    def make_global_action(self, args):
        '''
        可变参数，每个agent传递的action拼合成global_action
        :param args:
        :return:
        '''
        return []

    def get_reword(self, global_action, state):
        return 0


# 被执行迁移的服务
class Service:

    # 数据量，cpu周期，最大忍受延迟
    # 数据量是传输的时候用？
    # cpu周期是计算延迟的时候用？
    def __init__(self, data_size, require_cycle):
        self.data_size = data_size  # 迁移服务的数据大小
        self.require_cycle = require_cycle  # 任务计算所需的cpu周期数


# MEC环境
class MECEnv:
    # state (current user position[], 速度[], 方向[], 用户在和哪台服务器通信)

    def __init__(self, edge_server_list, user_list, time_slot_total):
        self.edge_server_list = edge_server_list
        self.edge_server_num = len(self.edge_server_list)
        self.user_list = user_list
        self.user_num = len(self.user_list)

        self.time_slot_now = 0
        self.time_slot_total = time_slot_total  # 待设定

        self.sdn = SDNController()

        # state 初始化时所有用户的服务都位于第一台服务器上
        # self.state = [[user_list[0], edge_server_list[0]], [user_list[1], edge_server_list[0]]]
        self.state = []
        # self.file = ""
        self.reword = 0

        # reword需要在这里写嘛

    def __reward(self):
        # 没有考虑服务迁移产生的延迟
        # 车辆与服务器延迟 服务计算延迟 和 cost
        pass

    def __get_user_communication_delay(self, user: User, server: EdgeServer, service: Service):
        # TODO
        # 考虑 迁移决策后的传输延迟 到底是跟哪个时隙计算的 （因该是当下服务器在哪，以及当下用户在哪。 迁移后的呢个时隙计算 也是如此）
        # 验证状态中的 server是否也要改

        '''
        用户与服务器请求service的通信延迟
        :param user: 用户
        :param server: 边缘服务器
        :param service: 具体服务
        :return: 数值
        '''
        '''
                tmp_rn = server.bandwidth_between_user_server_total * 1000 / len(server.user_list) * 1000  # W / K 速率公式的一部分
        mw = pow(10, -174 / 10) * 0.001  # 噪声功率转化 -174dbm 转成瓦特 No 信噪比
        '''

        communication_distance = user.location.get_abs_distance(server.location)
        # if communication_distance == 0:
        #     return 0
        '''
                h = 0.001 * pow(communication_distance, -3)  # 无线增益
        # 数据传输率
        data_trans_rate = tmp_rn * math.log2(
            1 + server.pn * h / (tmp_rn * mw))  # 计算速率 (1 + transmission power *无线信道增益 /  信噪比)
        communication_delay = service.data_size * 1024 / data_trans_rate
        '''

        return -10 * communication_distance

    def __get_computing_delay(self, service: Service, server: EdgeServer):
        '''
        服务的计算延迟
        :param service:
        :param server:
        :return:
        '''
        return service.require_cycle / server.F

    # 考虑了迁移成本 没有考虑迁移延迟
    def __get_migration_cost(self, server_from: EdgeServer, server_to: EdgeServer):
        migration_distance = server_from.location.get_abs_distance(server_to.location)
        return Constant.migration_cost_item * migration_distance
        # 论文中没给公式
        # 能量消耗，

    def __get_migration_cost(self):
        return np.random.normal(Constant.migration_cost_param.get("loc"), Constant.migration_cost_param.get("sigma"), 1)

    def __read_state_from_file(self, ):
        '''
        从文件里获取state的用户位置，方向，速度等
        :param file:
        :return:
        '''
        return []

    # 动作空间是咋样的：
    # 根据action更新state ,global action[[0,1],[1,0],[0,0] Ai,j 第i个服务器上部署的是第j个服务
    def step(self, action):

        # 距离是根据哪个state算的
        self.time_slot_now += 1
        if self.time_slot_now >=500:
            return self.state, self.reword, True, {}


        now_state = self.state
        # todo 少一个速度 目前是 [pint_x, point_y,
        next_state = [[row[self.time_slot_now].user_id, row[self.time_slot_now].location.point_x,
                       row[self.time_slot_now].location.point_y, row[self.time_slot_now].service.data_size,
                       row[self.time_slot_now].service.require_cycle] for row in self.user_list]



        next_reword = 0

        if not self.sdn.check_action(action):
            next_reword = Constant.not_feasible_penalty
            # 只更新 用户位置 服务
            # next_state[:, server_position] = self.state[:, server_position
            for idx, row in enumerate(self.state):
                next_state[idx].append(row[-1])
        else:
            # 更新server的user_list字段
            # for i in range(self.edge_server_num):
            #     for j in range(self.user_num):
            #         if action[j] == i:
            #             # state 里只添加edgeid行吗？
            #             next_state[j].append(self.edge_server_list[i].edge_id)  # 添加该服务所部署在哪个服务器上
            #             self.edge_server_list[i].user_list.append(self.user_list[j][self.time_slot_now])
            for i in range(self.user_num):
                server_mig = action[i]  # 决策要将user_i的服务迁移到server_mig上
                next_state[i].append(self.edge_server_list[server_mig].edge_id)  # 添加该服务所部署在哪个服务器上
                self.edge_server_list[server_mig].user_list.append(self.user_list[i][self.time_slot_now])

            # TODO 这段代码是否可转换为矩阵运算
            # for i in range(self.user_num):
            #     for j in range(self.edge_server_num):
            #         if action[j][i] != 1:
            #             continue
            #         # next_server = self.edge_server_list[j]
            #         now_server = self.edge_server_list[now_state[i][Constant.state_server_id_index]]
            #         user = self.user_list[i][self.time_slot_now]
            #         next_reword += -(self.__get_user_communication_delay(user, now_server, user.service)
            #                          # + self.__get_computing_delay(user.service, next_server)
            #                          # + self.__get_migration_cost()
            #                          )
            for i in range(self.user_num):
                # server_mig = action[i]  # 决策要将user_i的服务迁移到server_mig上
                now_server = self.edge_server_list[now_state[i][Constant.state_server_id_index]]
                user = self.user_list[i][self.time_slot_now]
                next_reword += -(self.__get_user_communication_delay(user, now_server, user.service))

        # TODO 完善State
        self.state = next_state
        self.reword = next_reword
        return self.state, self.reword, False, {}

    def reset(self):
        self.time_slot_now = 0
        self.state = 0
        self.reword = 0
        self.state = [[0, 0, 0, 50, 20, 0], [0, 0, 0, 50, 20, 0]]
        state, _, _, _ = self.step([0, 0])
        return state


if __name__ == "__main__":
    edge_server_list = []

    edge_server_list.append(EdgeServer(0, Point(500, 9), 30, 10))
    edge_server_list.append(EdgeServer(1, Point(2500, 9), 30, 10))
    edge_server_list.append(EdgeServer(2, Point(4500, 9), 30, 10))

    service = Service(50, 20)

    user_list = [[], []]
    time_slot = 5000 / 100
    for i in range(1000):
        user_list[0].append(User(0, Point((time_slot * i)%5000, 9), service))

    env = MECEnv(edge_server_list, user_list, 100)

    state, reword, _, _ = env.step([[1, 0], [0, 1], [0, 0]])

    state2, reword2, _, _ = env.step([[0, 0], [0, 0], [1, 1]])

    print("state:\n", state)
    print("reword:\n", reword)
    print("state2:\n", state2)
    print("reword2:\n", reword2)
